package com.talkyun.openx.ocean;

import com.talkyun.ocean.RegistryClient;
import com.talkyun.ocean.model.Provider;
import com.talkyun.ocean.model.Service;
import com.talkyun.openx.ocean.base.BaseHttpFilter;
import com.talkyun.openx.ocean.provider.ProviderManager;

import com.talkyun.openx.ocean.wrapper.HttpInterceptorHandler;
import com.talkyun.openx.ocean.wrapper.RegistryableServiceFactory;
import com.talkyun.openx.ocean.wrapper.RegistryableServletHandler;
import com.talkyun.openx.server.core.Interceptor;
import com.talkyun.openx.server.helper.VersionHelper;

import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.util.List;
import java.util.Map;

public class OceanRestfulFilter extends BaseHttpFilter {
    private String version = VersionHelper.getOpenxVersion();
    private ProviderManager pm = new ProviderManager();
    private RegistryableServletHandler handler;
    private RegistryClient registryClient = null;

    private HttpInterceptorHandler httpHandler;

    @Override
    @SuppressWarnings("unchecked")
    public void init(FilterConfig fc) throws ServletException {
        super.init(fc);
        System.out.println("\n");

        Provider provider;
        try {
            provider = pm.getServiceProviderFromTomcat(fc.getServletContext());
            System.setProperty("openx.http.port", Integer.toString(provider.getPort()));
            logger.info("The tomcat as provider: {}", provider);
        } catch (Throwable t) {
            logger.error("Error to get provider!", t);
            throw new ServletException(t);
        }

        // registry service to center
        RegistryClient client = this.getRegistryClient(fc);

        ApplicationContext springCtx = WebApplicationContextUtils.getWebApplicationContext(fc.getServletContext());
        if (springCtx == null) {
            throw new RuntimeException("Not found spring context!");
        }

        //  - find all exported spring bean
        Map map = springCtx.getBeansOfType(OceanRestfulScanner.class);
        if (map == null || map.size() != 1) {
            throw new RuntimeException("Only support one scanner bean! " + map.size());
        }
        // registry call
        OceanRestfulScanner scanner = (OceanRestfulScanner) ((Map.Entry) map.entrySet().iterator().next()).getValue();
        List<Service> services = scanner.getAllServiceInfo();
        List<Interceptor> interceptors = scanner.getRestInterceptor();
        client.registry(provider, services);

        // build service handler
        handler = new RegistryableServletHandler(fc.getServletContext(), new RegistryableServiceFactory(services, interceptors));
        httpHandler = new HttpInterceptorHandler(fc.getServletContext(), scanner.getHttpInterceptor());

        logger.info("-> Ocean-able openx {} is ready.", version);
        System.out.println("-------------------------------------------------------------------------------\n");
    }

    @Override
    protected void doHttpFilter(HttpServletRequest req, HttpServletResponse resp, FilterChain fc) throws IOException, ServletException {
        if (httpHandler != null) {
            httpHandler.handle(req, resp, fc);
        }
    }

    @Override
    protected void doRestFilter(ServletRequest req, ServletResponse resp, FilterChain fc) throws IOException {
        handler.handle((HttpServletRequest) req, (HttpServletResponse) resp);
    }

    @Override
    public void destroy() {
        super.destroy();
    }

    private RegistryClient getRegistryClient(FilterConfig fc) {
        if (registryClient != null) {
            return registryClient;
        }

        // build registry client
        String url = super.getInitParameter(fc, "oceanServerUrl", "openxServerUrl");
        if (super.isBlank(url)) {
            logger.info("No ocean server, using LocalRegistryManager!");
            registryClient = new RegistryClient();
        } else {
            logger.info("Using ocean server: {}", url);
            registryClient = new RegistryClient(url.trim());
        }
        return registryClient;
    }
}